/*
 * Copyright (C) 2008 Eric Bottard
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *         http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.code.liquidform.internal;

import com.google.code.liquidform.SubQuery;
import com.google.code.liquidform.internal.managedobjects.AliasedAbstractSchemaName;
import com.google.code.liquidform.internal.managedobjects.AliasedAssociationPath;
import com.google.code.liquidform.internal.managedobjects.AliasedInCollection;
import com.google.code.liquidform.internal.managedobjects.AssociationPath;
import com.google.code.liquidform.internal.managedobjects.CollectionAssociationPath;
import com.google.code.liquidform.internal.managedobjects.FrameworkManagedObject;

public class AliasFactory {

	private final static ThreadLocal<WeakIdentityHashMap<Object, FrameworkManagedObject>> bindings = new ThreadLocal<WeakIdentityHashMap<Object, FrameworkManagedObject>>() {
		@Override
		protected WeakIdentityHashMap<Object, FrameworkManagedObject> initialValue() {
			return new WeakIdentityHashMap<Object, FrameworkManagedObject>();
		}
	};

	public static void rememberAsAbstractSchemaName(Object frameworkObject,
			String type, String alias) {
		bindings().put(frameworkObject,
				new AliasedAbstractSchemaName(type, alias));
	}

	public static <E> void rememberAsCollectionAssociationPath(
			Object frameworkObject, String path, Class<E> elementType,
			E elementProxy) {
		bindings().put(
				frameworkObject,
				new CollectionAssociationPath<E>(path, elementType,
						elementProxy));
	}

	public static void rememberAsAssociationPath(Object frameworkObject,
			String path) {
		bindings().put(frameworkObject, new AssociationPath(path));
	}

	public static void rememberAsAliasedAssociationPath(Object frameworkObject,
			String path, String alias) {
		bindings()
				.put(frameworkObject, new AliasedAssociationPath(path, alias));
	}

	public static void rememberAsAliasedInCollection(Object frameworkObject,
			String path, String alias) {
		bindings().put(frameworkObject, new AliasedInCollection(path, alias));
	}

	public static FrameworkManagedObject about(Object frameworkObject) {
		FrameworkManagedObject ret = safeAbout(frameworkObject);
		if (ret == null) {
			throw new IllegalStateException("Nothing is known about "
					+ frameworkObject);
		}
		return ret;
	}

	public static FrameworkManagedObject safeAbout(Object frameworkObject) {
		return bindings().get(frameworkObject);
	}

	private static WeakIdentityHashMap<Object, FrameworkManagedObject> bindings() {
		return bindings.get();
	}

	public static void resetBindings() {
		bindings.set(new WeakIdentityHashMap<Object, FrameworkManagedObject>());
	}

	public static void remember(Object frameworkObject,
			FrameworkManagedObject info) {
		bindings().put(frameworkObject, info);
	}

	public static String safeStringForm(Object what) {
		// TODO : there is no such thing as DateLiteral in JPA
		// ==> if what is a Date, but there is no FMO beneath it => error

		if (what instanceof FrameworkManagedObject) {
			FrameworkManagedObject fmo = (FrameworkManagedObject) what;
			return fmo.getPreferredStringRepresentation();
		}
		FrameworkManagedObject about = safeAbout(what);
		if (about == null) {
			if (String.class.equals(what.getClass())) {
				return escape((String) what);
			} else if (Character.class.equals(what.getClass())) {
				return escape(what.toString());
			} else if (what instanceof SubQuery) {
				// Note : not the most correct place to handle the () issue
				// but gets the job done as long as SubQueries always appear
				// with () around'em
				return "(" + what.toString() + ")";
			} else {
				return what.toString();
			}
		} else {
			return about.getPreferredStringRepresentation();
		}
	}

	private static String escape(String what) {
		return "'" + what.replaceAll("'", "''") + "'";
	}

}
